Frustum = class()

function Frustum:init()
    self.m = {}
    for s = 1,6 do
        self.m[s] = {}
        for i = 1,4 do
            self.m[s][i] = 0
        end
    end
    self:calculate()
end

function Frustum:calculate()
    local proj = projectionMatrix()
    local modl = viewMatrix()
    local clip = modl * proj
    local m = self.m
    m[1][1] = clip[4] - clip[1]
    m[1][2] = clip[8] - clip[5]
    m[1][3] = clip[12] - clip[9]
    m[1][4] = clip[16] - clip[13]
    self:normalizePlane(m,1)
    m[2][1] = clip[4] + clip[1]
    m[2][2] = clip[8] + clip[5]
    m[2][3] = clip[12] + clip[9]
    m[2][4] = clip[16] + clip[13]
    self:normalizePlane(m,2)
    m[3][1] = clip[4] + clip[2]
    m[3][2] = clip[8] + clip[6]
    m[3][3] = clip[12] + clip[10]
    m[3][4] = clip[16] + clip[14]
    self:normalizePlane(m,3)
    m[4][1] = clip[4] - clip[2]
    m[4][2] = clip[8] - clip[6]
    m[4][3] = clip[12] - clip[10]
    m[4][4] = clip[16] - clip[14]
    self:normalizePlane(m,4)
    m[5][1] = clip[4] - clip[3]
    m[5][2] = clip[8] - clip[7]
    m[5][3] = clip[12] - clip[11]
    m[5][4] = clip[16] - clip[15]
    self:normalizePlane(m,5)
    m[5][1] = clip[4] + clip[3]
    m[5][2] = clip[8] + clip[7]
    m[5][3] = clip[12] + clip[11]
    m[5][4] = clip[16] + clip[15]
    self:normalizePlane(m,6)
end

function Frustum:normalizePlane(frustum,side)
    local magnitude = math.sqrt(frustum[side][1] * frustum[side][1] + frustum[side][2] * frustum[side][2] + frustum[side][3] * frustum[side][3])
    frustum[side][1] = frustum[side][1] / magnitude
    frustum[side][2] = frustum[side][2] / magnitude
    frustum[side][3] = frustum[side][3] / magnitude
    frustum[side][4] = frustum[side][4] / magnitude
end

function Frustum:cubeInFrustum(x1,y1,z1,x2,y2,z2)
    local m = self.m
    for i=1,6 do
        if (m[i][1] * x1 + m[i][2] * y1 + m[i][3] * z1 + m[i][4] <= 0.0) and 
        (m[i][1] * x2 + m[i][2] * y1 + m[i][3] * z1 + m[i][4] <= 0.0) and 
        (m[i][1] * x1 + m[i][2] * y2 + m[i][3] * z1 + m[i][4] <= 0.0) and 
        (m[i][1] * x2 + m[i][2] * y2 + m[i][3] * z1 + m[i][4] <= 0.0) and 
        (m[i][1] * x1 + m[i][2] * y1 + m[i][3] * z2 + m[i][4] <= 0.0) and 
        (m[i][1] * x2 + m[i][2] * y1 + m[i][3] * z2 + m[i][4] <= 0.0) and 
        (m[i][1] * x1 + m[i][2] * y2 + m[i][3] * z2 + m[i][4] <= 0.0) and 
        (m[i][1] * x2 + m[i][2] * y2 + m[i][3] * z2 + m[i][4] <= 0.0) then
            return false
        end
    end
    return true
end

function Frustum:isVisible(aabb)
    return self:cubeInFrustum(aabb.x0,aabb.y0,aabb.z0,aabb.x1,aabb.y1,aabb.z1)
end
